Kattava opas TypeScript Compiler API:sta, joka kattaa abstraktit syntaksipuut (AST), koodianalyysin, muuntamisen ja generoinnin kansainvälisille kehittäjille.
TypeScript Compiler API: AST-manipulaation ja koodin muuntamisen hallinta
TypeScript Compiler API tarjoaa tehokkaan rajapinnan TypeScript- ja JavaScript-koodin analysointiin, manipulointiin ja generointiin. Sen ytimessä on abstrakti syntaksipuu (AST), joka on lähdekoodisi jäsennelty esitys. AST:n käsittelyn ymmärtäminen avaa mahdollisuuksia kehittyneiden työkalujen, kuten linttereiden, koodin muotoilijoiden, staattisten analysoijien ja mukautettujen koodin generaattoreiden rakentamiseen.
Mikä on TypeScript Compiler API?
TypeScript Compiler API on joukko TypeScript-rajapintoja ja -funktioita, jotka paljastavat TypeScript-kääntäjän sisäiset toiminnat. Sen avulla kehittäjät voivat ohjelmallisesti olla vuorovaikutuksessa kääntämisprosessin kanssa, ylittämällä pelkän koodin kääntämisen. Voit käyttää sitä seuraaviin tarkoituksiin:
- Analysoi koodia: Tarkasta koodin rakenne, tunnista mahdolliset ongelmat ja hae semanttista tietoa.
- Muunna koodia: Muokkaa olemassa olevaa koodia, lisää uusia ominaisuuksia tai jäsentä koodi automaattisesti.
- Generoi koodia: Luo uutta koodia tyhjästä mallien tai muun syötteen perusteella.
Tämä API on välttämätön kehittyneiden kehitystyökalujen rakentamisessa, jotka parantavat koodin laatua, automatisoivat toistuvia tehtäviä ja parantavat kehittäjien tuottavuutta.
Abstraktin syntaksipuun (AST) ymmärtäminen
AST on koodisi rakenteen puumainen esitys. Puun jokainen solmu edustaa syntaktista rakennetta, kuten muuttujan määrittelyä, funktiokutsua tai ohjausrakenteen lausetta. TypeScript Compiler API tarjoaa työkaluja AST:n läpikäyntiin, sen solmujen tarkasteluun ja niiden muokkaamiseen.
Harkitse tätä yksinkertaista TypeScript-koodia:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
Tämän koodin AST edustaisi funktion määrittelyä, palautuslausetta, mallinetta, console.log-kutsua ja muita koodin elementtejä. AST:n visualisointi voi olla haastavaa, mutta AST explorerin (astexplorer.net) kaltaiset työkalut voivat auttaa. Näiden työkalujen avulla voit syöttää koodia ja nähdä sen vastaavan AST:n käyttäjäystävällisessä muodossa. AST Explorerin käyttäminen auttaa sinua ymmärtämään sellaista koodirakennetta, jota aiot manipuloida.
Tärkeimmät AST-solmujen tyypit
TypeScript Compiler API määrittelee erilaisia AST-solmutyyppejä, joista jokainen edustaa erilaista syntaktista rakennetta. Tässä on joitain yleisiä solmutyyppejä:
- SourceFile: Edustaa kokonaista TypeScript-tiedostoa.
- FunctionDeclaration: Edustaa funktion määrittelyä.
- VariableDeclaration: Edustaa muuttujan määrittelyä.
- Identifier: Edustaa tunnusta (esim. muuttujan nimi, funktion nimi).
- StringLiteral: Edustaa merkkijonoliteraalia.
- CallExpression: Edustaa funktiokutsua.
- ReturnStatement: Edustaa palautuslausetta.
Jokaisella solmulla on ominaisuuksia, jotka antavat tietoa vastaavasta koodielementistä. Esimerkiksi `FunctionDeclaration`-solmulla voi olla ominaisuuksia sen nimestä, parametreista, palautustyypistä ja rungosta.
Compiler API:n käytön aloittaminen
Aloittaaksesi Compiler API:n käytön, sinun on asennettava TypeScript ja sinulla on oltava perustiedot TypeScript-syntaksista. Tässä on yksinkertainen esimerkki, joka osoittaa, miten TypeScript-tiedosto luetaan ja sen AST tulostetaan:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015, // Kohde-ECMAScript-versio
true // SetParentNodes: true säilyttääksesi pääviittaukset AST:ssä
);
function printAST(node: ts.Node, indent = 0) {
const indentStr = " ".repeat(indent);
console.log(`${indentStr}${ts.SyntaxKind[node.kind]}`);
node.forEachChild(child => printAST(child, indent + 1));
}
printAST(sourceFile);
Selitys:
- Tuo moduuleja: Tuo `typescript`-moduulin ja `fs`-moduulin tiedostojärjestelmätoimintoja varten.
- Lue lähdetiedosto: Lukee `example.ts`-nimisen TypeScript-tiedoston sisällön. Sinun on luotava `example.ts`-tiedosto, jotta tämä toimisi.
- Luo SourceFile: Luo `SourceFile`-objektin, joka edustaa AST:n juurta. `ts.createSourceFile`-funktio jäsentää lähdekoodin ja generoi AST:n.
- Tulosta AST: Määrittelee rekursiivisen funktion `printAST`, joka käy läpi AST:n ja tulostaa kunkin solmun tyypin.
- Kutsu printAST: Kutsuu `printAST`-funktiota ja aloittaa AST:n tulostamisen juuresta, `SourceFile`-solmusta.
Voit suorittaa tämän koodin tallentamalla sen `.ts`-tiedostona (esim. `ast-example.ts`), luomalla `example.ts`-tiedoston jollakin TypeScript-koodilla ja sitten kääntämällä ja suorittamalla koodin:
tsc ast-example.ts
node ast-example.js
Tämä tulostaa `example.ts`-tiedostosi AST:n konsoliin. Tuloste näyttää solmujen hierarkian ja niiden tyypit. Esimerkiksi se voi näyttää `FunctionDeclaration`, `Identifier`, `Block` ja muita solmutyyppejä.
AST:n läpikäynti
Compiler API tarjoaa useita tapoja AST:n läpikäyntiin. Yksinkertaisin tapa on käyttää `forEachChild`-metodia, kuten edellisessä esimerkissä esitettiin. Tämä metodi käy läpi annetun solmun jokaisen lapsisolmun.
Monimutkaisemmille läpikäyntitapauksille voit käyttää `Visitor`-mallia. Vierailija on objekti, joka määrittelee metodit, jotka kutsutaan tietyille solmutyypeille. Tämän avulla voit mukauttaa läpikäyntiprosessia ja suorittaa toimintoja solmun tyypin perusteella.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
class IdentifierVisitor {
visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
console.log(`Löydetty tunnus: ${node.text}`);
}
ts.forEachChild(node, n => this.visit(n));
}
}
const visitor = new IdentifierVisitor();
visitor.visit(sourceFile);
Selitys:
- IdentifierVisitor-luokka: Määrittelee `IdentifierVisitor`-luokan, jolla on `visit`-metodi.
- Visit-metodi: `visit`-metodi tarkistaa, onko nykyinen solmu `Identifier`. Jos on, se tulostaa tunnuksen tekstin. Sen jälkeen se kutsuu rekursiivisesti `ts.forEachChild` ja käy läpi lapsisolmut.
- Luo vierailija: Luo `IdentifierVisitor`:in instanssi.
- Aloita läpikäynti: Kutsuu `visit`-metodia `SourceFile`-objektilla ja aloittaa läpikäynnin.
Tämä esimerkki osoittaa, miten löytää kaikki tunnukset AST:stä. Voit mukauttaa tätä mallia löytääksesi muita solmutyyppejä ja suorittaaksesi erilaisia toimintoja.
AST:n muuntaminen
Compiler API:n todellinen voima piilee sen kyvyssä muuntaa AST:tä. Voit muokata AST:tä muuttaaksesi koodisi rakennetta ja toimintaa. Tämä on pohja koodin jäsentäjille, koodin generaattoreille ja muille kehittyneille työkaluille.
AST:n muuntamiseksi sinun on käytettävä `ts.transform`-funktiota. Tämä funktio ottaa `SourceFile`:n ja luettelon `TransformerFactory`-funktioista. `TransformerFactory` on funktio, joka ottaa `TransformationContext`:in ja palauttaa `Transformer`-funktion. `Transformer`-funktio vastaa solmujen läpikäynnistä ja muuntamisesta AST:ssä.
Tässä on yksinkertainen esimerkki, joka osoittaa, miten lisätään kommentti TypeScript-tiedoston alkuun:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
const transformerFactory: ts.TransformerFactory = context => {
return transformer => {
return node => {
if (ts.isSourceFile(node)) {
// Luo johtava kommentti
const comment = ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
" Tämä tiedosto muunnettiin automaattisesti ",
true // hasTrailingNewLine
);
return node;
}
return node;
};
};
};
const { transformed } = ts.transform(sourceFile, [transformerFactory]);
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed
});
const result = printer.printFile(transformed[0]);
fs.writeFileSync("example.transformed.ts", result);
Selitys:
- TransformerFactory: Määrittelee `TransformerFactory`-funktion, joka palauttaa `Transformer`-funktion.
- Transformer: `Transformer`-funktio tarkistaa, onko nykyinen solmu `SourceFile`. Jos on, se lisää johtavan kommentin solmuun käyttämällä `ts.addSyntheticLeadingComment`.
- ts.transform: Kutsuu `ts.transform`-funktiota muunnoksen soveltamiseksi `SourceFile`:iin.
- Tulostin: Luo `Printer`-objektin koodin generointiin muunnetusta AST:stä.
- Tulosta ja kirjoita: Tulostaa muunnetun koodin ja kirjoittaa sen uuteen tiedostoon nimeltä `example.transformed.ts`.
Tämä esimerkki osoittaa yksinkertaisen muunnoksen, mutta voit käyttää samaa mallia suorittamaan monimutkaisempia muunnoksia, kuten koodin jäsentämistä, lokilausekkeiden lisäämistä tai dokumentaation generointia.
Kehittyneet muunnustekniikat
Tässä on joitain kehittyneitä muunnustekniikoita, joita voit käyttää Compiler API:n kanssa:
- Uusien solmujen luominen: Käytä `ts.createXXX`-funktioita luomaan uusia AST-solmuja. Esimerkiksi `ts.createVariableDeclaration` luo uuden muuttujan määrittelysolmun.
- Solmujen korvaaminen: Korvaa olemassa olevat solmut uusilla solmuilla käyttämällä `ts.visitEachChild`-funktiota.
- Solmujen lisääminen: Lisää uusia solmuja AST:hen käyttämällä `ts.updateXXX`-funktioita. Esimerkiksi `ts.updateBlock` päivittää lohkolausuman uusilla lauseilla.
- Solmujen poistaminen: Poista solmut AST:stä palauttamalla `undefined` transformer-funktiosta.
Koodin generointi
Kun olet muuntanut AST:n, sinun on generoitava koodi siitä. Compiler API tarjoaa `Printer`-objektin tätä tarkoitusta varten. `Printer` ottaa AST:n ja luo koodin merkkijonoesityksen.
`ts.createPrinter`-funktio luo `Printer`-objektin. Voit määrittää tulostimen eri asetuksilla, kuten käytettävän rivinvaihtomerkin ja sen, pitäisikö kommentteja tuottaa.
`printer.printFile`-metodi ottaa `SourceFile`:n ja palauttaa koodin merkkijonoesityksen. Voit sitten kirjoittaa tämän merkkijonon tiedostoon.
Compiler API:n käytännön sovellukset
TypeScript Compiler API:llä on lukuisia käytännön sovelluksia ohjelmistokehityksessä. Tässä on muutamia esimerkkejä:
- Lintterit: Rakenna mukautettuja linttereitä pakottamaan koodausstandardit ja tunnistamaan potentiaaliset ongelmat koodissasi.
- Koodin muotoilijat: Luo koodin muotoilijoita muotoilemaan koodisi automaattisesti tietyn tyylioppaan mukaisesti.
- Staattiset analysoijat: Kehitä staattisia analysoijia havaitsemaan virheitä, tietoturva-aukkoja ja suorituskyvyn pullonkauloja koodissasi.
- Koodin generaattorit: Generoi koodia malleista tai muusta syötteestä, automatisoiden toistuvia tehtäviä ja vähentäen boilerplate-koodia. Esimerkiksi API-asiakkaiden tai tietokantakaavioiden generointi kuvaustiedostosta.
- Jäsentämistyökalut: Rakenna jäsentämistyökaluja muuttujien automaattiseen nimeämiseen, funktioiden poimimiseen tai koodin siirtämiseen tiedostojen välillä.
- Kansainvälistämisen (i18n) automatisointi: Poimi automaattisesti käännettävät merkkijonot TypeScript-koodistasi ja luo lokalisointitiedostot eri kielille. Esimerkiksi työkalu voisi skannata koodin `translate()`-funktiolle välitetyt merkkijonot ja lisätä ne automaattisesti käännösresurssitiedostoon.
Esimerkki: Yksinkertaisen lintterin rakentaminen
Luodaan yksinkertainen lintteri, joka tarkistaa käyttämättömät muuttujat TypeScript-koodissa. Tämä lintteri tunnistaa muuttujat, jotka on määritelty, mutta joita ei koskaan käytetä.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
function findUnusedVariables(sourceFile: ts.SourceFile) {
const usedVariables = new Set();
function visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
usedVariables.add(node.text);
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
const unusedVariables: string[] = [];
function checkVariableDeclaration(node: ts.Node) {
if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
const variableName = node.name.text;
if (!usedVariables.has(variableName)) {
unusedVariables.push(variableName);
}
}
ts.forEachChild(node, checkVariableDeclaration);
}
checkVariableDeclaration(sourceFile);
return unusedVariables;
}
const unusedVariables = findUnusedVariables(sourceFile);
if (unusedVariables.length > 0) {
console.log("Käyttämättömät muuttujat:");
unusedVariables.forEach(variable => console.log(`- ${variable}`));
} else {
console.log("Käyttämättömiä muuttujia ei löytynyt.");
}
Selitys:
- findUnusedVariables-funktio: Määrittelee funktion `findUnusedVariables`, joka ottaa `SourceFile`:n syötteenä.
- usedVariables-joukko: Luo `Set`:in tallentamaan käytettyjen muuttujien nimet.
- visit-funktio: Määrittelee rekursiivisen funktion `visit`, joka käy läpi AST:n ja lisää kaikkien tunnusten nimet `usedVariables`-joukkoon.
- checkVariableDeclaration-funktio: Määrittelee rekursiivisen funktion `checkVariableDeclaration`, joka tarkistaa, onko muuttujan määrittely käyttämätön. Jos on, se lisää muuttujan nimen `unusedVariables`-taulukkoon.
- Return unusedVariables: Palauttaa taulukon, joka sisältää käyttämättömien muuttujien nimet.
- Tuloste: Tulostaa käyttämättömät muuttujat konsoliin.
Tämä esimerkki osoittaa yksinkertaisen lintterin. Voit laajentaa sitä tarkistamaan muita koodausstandardeja ja tunnistamaan muita potentiaalisia ongelmia koodissasi. Voit esimerkiksi tarkistaa käyttämättömät tuonnit, liian monimutkaiset funktiot tai mahdolliset tietoturva-aukot. Tärkeintä on ymmärtää, miten AST:tä käydään läpi ja tunnistetaan tietyt solmutyypit, joista olet kiinnostunut.
Parhaat käytännöt ja huomioon otettavat asiat
- Ymmärrä AST: Käytä aikaa AST:n rakenteen ymmärtämiseen. Käytä AST explorerin kaltaisia työkaluja koodisi AST:n visualisoimiseen.
- Käytä tyyppisuojauksia: Käytä tyyppisuojauksia (`ts.isXXX`) varmistaaksesi, että työskentelet oikeiden solmutyyppien kanssa.
- Harkitse suorituskykyä: AST-muunnokset voivat olla laskennallisesti kalliita. Optimoi koodisi minimoimaan läpikäytävien ja muunnettavien solmujen määrä.
- Käsittele virheet: Käsittele virheet hallitusti. Compiler API voi heittää poikkeuksia, jos yrität suorittaa virheellisiä toimintoja AST:llä.
- Testaa perusteellisesti: Testaa muunnoksesi perusteellisesti varmistaaksesi, että ne tuottavat halutut tulokset ja eivät tuo uusia virheitä.
- Käytä olemassa olevia kirjastoja: Harkitse olemassa olevien kirjastojen käyttöä, jotka tarjoavat korkeamman tason abstraktioita Compiler API:n yli. Nämä kirjastot voivat yksinkertaistaa yleisiä tehtäviä ja vähentää kirjoitettavan koodin määrää. Esimerkkejä ovat `ts-morph` ja `typescript-eslint`.
Johtopäätös
TypeScript Compiler API on tehokas työkalu kehittyneiden kehitystyökalujen rakentamiseen. Ymmärtämällä, miten AST:tä käsitellään, voit luoda linttereitä, koodin muotoilijoita, staattisia analysoijia ja muita työkaluja, jotka parantavat koodin laatua, automatisoivat toistuvia tehtäviä ja parantavat kehittäjien tuottavuutta. Vaikka API voi olla monimutkainen, sen hallitsemisen hyödyt ovat merkittäviä. Tämä kattava opas tarjoaa perustan Compiler API:n tehokkaalle tutkimiselle ja hyödyntämiselle projekteissasi. Muista hyödyntää AST Explorerin kaltaisia työkaluja, käsitellä solmutyypit huolellisesti ja testata muunnoksesi perusteellisesti. Harjoittelun ja omistautumisen avulla voit avata TypeScript Compiler API:n täyden potentiaalin ja rakentaa innovatiivisia ratkaisuja ohjelmistokehityksen maisemaan.
Lisätutkimus:
- TypeScript Compiler API -dokumentaatio: [https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API)
- AST Explorer: [https://astexplorer.net/](https://astexplorer.net/)
- ts-morph-kirjasto: [https://ts-morph.com/](https://ts-morph.com/)
- typescript-eslint: [https://typescript-eslint.io/](https://typescript-eslint.io/)